﻿using System;
using System.Collections.Generic;

namespace IFramework
{
    public class PoolManager : Singleton<PoolManager>,IPoolManager
    {
        private Dictionary<object, List<IPoolObj>> parentPool;
        private Dictionary<Type, Pool> pools;
        private Dictionary<IPoolObj, PoolObjBox> poolObjInfo;
        private PoolObjBoxCreater boxCreater;
        public override void OnSingletonInit()
        {
            pools = new Dictionary<Type, Pool>();
            parentPool = new Dictionary<object, List<IPoolObj>>();
            poolObjInfo = new Dictionary<IPoolObj, PoolObjBox>();
            boxCreater = new PoolObjBoxCreater(100);
        }
        public override void Dispose()
        {
            base.Dispose();
            boxCreater.Clean();
            pools.Clear();
            parentPool.Clear();
            poolObjInfo.Clear();
            boxCreater = null;
            poolObjInfo = null;
            parentPool = null;
            pools = null;
        }

        private void CheckDictionaryKey(Type dicType, object parentObj,IPoolObj obj)
        {
            if (!pools.ContainsKey(dicType))
                pools.Add(dicType, new Pool(dicType, 4));
            if (!parentPool.ContainsKey(parentObj))
                parentPool.Add(parentObj, new List<IPoolObj>());
            if (!poolObjInfo.ContainsKey(obj))
                poolObjInfo.Add(obj, boxCreater.GetBox(obj,parentObj,dicType));
            lock (poolObjInfo[obj])
            {
                object oldParent = poolObjInfo[obj].ObjParent;
                if (parentObj != oldParent)
                {
                    if (oldParent != null)
                    {
                        parentPool[oldParent].Remove(obj);
                    }
                    poolObjInfo[obj].SetPrent(parentObj);
                    parentPool[parentObj].Add(obj);
                }
            }
        }
        public T GetPoolObj<T>(object PoolObjParent, params object[] paras) where T : IPoolObj, new()
        {
            T temp = pools[typeof(T)].GetPoolObj<T>(PoolObjParent, paras);
            CheckDictionaryKey(typeof(T), PoolObjParent,temp);
            return temp;
        }
        public T GetInitObj<T>(T prefab, object PoolObjParent, params object[] paras) where T : UnityEngine.Object, IPoolObj
        {
            T temp = pools[prefab.GetType()].GetInitObj<T>(prefab,PoolObjParent, paras);
            CheckDictionaryKey(prefab.GetType(), PoolObjParent, temp);
            return temp;
        }

        public void Restore(Type poolType, IPoolObj poolObj, object poolObjParent, params object[] paras)
        {
            if (!poolObjInfo.ContainsKey(poolObj)) return;
            if (!parentPool.ContainsKey(poolObjParent)) return;
            if (!pools.ContainsKey(poolType)) return;
            lock (poolObjInfo[poolObj])
            {
                object parentObj = poolObjInfo[poolObj].ObjParent;
                if (parentObj != poolObjParent)
                {
                    throw new Exception("Parent Fault");
                }
            }
            lock (pools[poolType])
            {
                pools[poolType].Restore(poolObj, paras);
            }
        }
        public void Restore(Type poolType, IPoolObj poolObj, params object[] paras)
        {
            if (!poolObjInfo.ContainsKey(poolObj)) return;
            if (!pools.ContainsKey(poolType)) return;
            if (!parentPool.ContainsKey(poolObjInfo[poolObj].ObjParent)) return;
            Restore(poolType, poolObj, poolObjInfo[poolObj].ObjParent, paras);
        }
        public void Restore(Type poolType, object poolObjParent, params object[] paras)
        {
            if (!parentPool.ContainsKey(poolObjParent)) return;
            lock (parentPool[poolObjParent])
            {
                for (int i = 0; i < parentPool[poolObjParent].Count; i++)
                {
                    if (!poolObjInfo.ContainsKey(parentPool[poolObjParent][i])) continue;
                    if (poolObjInfo[parentPool[poolObjParent][i]].PoolObjType == poolType)
                    {
                        Restore(poolType,parentPool[poolObjParent][i],poolObjParent, paras);
                    }
                }
            }
        }
        public void Restore(Type poolType, params object[] paras)
        {
            lock (pools)
            {
                if (!pools.ContainsKey(poolType)) return;
            }
            lock (pools[poolType])
            {
                pools[poolType].RestoreAll(paras);
            }
        }
        public void Restore(Type poolType, IPoolObj[] poolObjs, object poolObjParent, params object[] paras)
        {
            for (int i = 0; i < poolObjs.Length; i++)
            {
                Restore(poolType, poolObjs[i], poolObjParent, paras);
            }
        }
        public void Restore(Type poolType, IPoolObj[] poolObjs, params object[] paras)
        {
            for (int i = 0; i < poolObjs.Length; i++)
            {
                Restore(poolType, poolObjs[i], paras);
            }
        }

        public void Restore(IPoolObj[] poolObjs, params object[] paras)
        {
            for (int i = 0; i < poolObjs.Length; i++)
            {
                Restore(poolObjs[i], paras);
            }
        }
        public void Restore(IPoolObj poolObj, object poolObjParent, params object[] paras)
        {
            if (!poolObjInfo.ContainsKey(poolObj)) return;
            if (!parentPool.ContainsKey(poolObjParent)) return;
            Type objType;
            lock(poolObjInfo)
            {
                objType = poolObjInfo[poolObj].PoolObjType;
            }
            Restore(objType, poolObj, poolObjParent, paras);
        }
        public void Restore(IPoolObj poolObj, params object[] paras)
        {
            if (!poolObjInfo.ContainsKey(poolObj)) return;
            object objParent;
            lock (poolObjInfo)
            {
                objParent = poolObjInfo[poolObj].ObjParent;
            }
            Restore(poolObj, objParent, paras);
        }
        public void Restore(object poolObjParent, params object[] paras)
        {
            lock (parentPool)
            {
                if (!parentPool.ContainsKey(poolObjParent)) return;
            }
            lock (parentPool[poolObjParent])
            {
                for (int i = 0; i < parentPool[poolObjParent].Count; i++)
                {
                    Restore(parentPool[poolObjParent][i], poolObjParent, paras);
                }
            }
        }
        public void Restore<T>(IPoolObj poolObj, object poolObjParent, params object[] paras)
        {
            if (!poolObjInfo.ContainsKey(poolObj)) return;
            Type objType = poolObjInfo[poolObj].PoolObjType;
            Restore(objType, poolObj, poolObjParent, paras);
        }
        public void Restore<T>(IPoolObj poolObj, params object[] paras)
        {
            if (!poolObjInfo.ContainsKey(poolObj)) return;
            object objPrent;
            objPrent = poolObjInfo[poolObj].ObjParent;
            Restore<T>(poolObj, objPrent, paras);
        }
        public void Restore<T>(IPoolObj[] poolObjs, object poolObjParent, params object[] paras)
        {
            for (int i = 0; i < poolObjs.Length; i++)
            {
                 Restore<T>(poolObjs[i],poolObjParent, paras);
            }
        }
        public void Restore<T>(IPoolObj[] poolObjs, params object[] paras) 
        {
            for (int i = 0; i < poolObjs.Length; i++)
            {
                Restore<T>(poolObjs[i],poolObjInfo[poolObjs[i]].ObjParent , paras);
            }
        }

        public void Restore<T>(object poolObjParent, params object[] paras)
        {
            Restore(typeof(T), poolObjParent, paras);
        }
        public void Restore<T>(params object[] paras)
        {
            if (!pools.ContainsKey(typeof(T))) return;
            Restore(typeof(T), paras);
        }


        public void Clean<T>(params object[] paras)
        {
            if (!pools.ContainsKey(typeof(T))) return;
            pools[typeof(T)].Clean(paras);
        }

        public void Clean(Type type, params object[] paras)
        {
            if (!pools.ContainsKey(type)) return;
            pools[type].Clean(paras);
        }
        public void Clean(IPoolObj[] poolObjs, params object[] paras)
        {
            for (int i = 0; i < poolObjs.Length; i++)
            {
                Clean(poolObjs[i], paras);
            }
        }

        public void Clean(IPoolObj poolObj, params object[] paras)
        {
            if (!poolObjInfo.ContainsKey(poolObj)) return;
            lock (poolObjInfo[poolObj])
            {
                Type type = poolObjInfo[poolObj].PoolObjType;
                if (pools.ContainsKey(type)) return;
                lock (pools[type])
                {
                    pools[type].Clean(poolObj, paras);
                }
            }
        }
        public void Clean(object parentObj, params object[] paras)
        {
            if (!parentPool.ContainsKey(parentObj)) return;
            lock (parentPool)
            {
                for (int i = 0; i < parentPool[parentObj].Count; i++)
                {
                    IPoolObj poolObj = parentPool[parentObj][i];
                    if (!poolObjInfo.ContainsKey(poolObj)) continue;
                    lock (poolObjInfo)
                    {
                        Type poolType = poolObjInfo[poolObj].PoolObjType;
                        if (!pools.ContainsKey(poolType)) continue;
                        lock (pools[poolType])
                        {
                            pools[poolType].Clean(poolObj, paras);
                        }
                    }

                }
            }

        }
        public void OnClean(IPoolObj poolObj)
        {
            if (poolObjInfo.ContainsKey(poolObj))
            {
                object objParent = poolObjInfo[poolObj].ObjParent;
                if (parentPool[objParent].Contains(poolObj))
                {
                    parentPool[objParent].Remove(poolObj);
                }
                boxCreater.RestoreBox(poolObjInfo[poolObj]);
                poolObjInfo.Remove(poolObj);
            }
        }
      
    }
}